package beta

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/container/glist"
	"github.com/gogf/gf/v2/container/gqueue"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/panjf2000/ants/v2"
	"math/rand"
	"sync"
	"time"
)

type TranslationRequestBucket struct {
	Content    string
	RetryLimit int
	RetryCount int
}

type ApiCallParam struct {
	ctx         context.Context
	bucketQueue *gqueue.Queue
	resultList  *glist.List // holds the request entity for simplifying testing
	wg          *sync.WaitGroup
	pool        *ants.PoolWithFunc
}

func CallOpenAiApi(input interface{}) {
	param := input.(*ApiCallParam)
	defer func() {
		param.wg.Done()
	}()

	item := param.bucketQueue.Pop()
	if item == nil {
		return
	}
	requestBucket := item.(*TranslationRequestBucket)

	triggerNextAction := func() {
		_ = param.pool.Invoke(param)
	}

	mimicSeed := rand.Intn(4) // data range is [0, 3)

	if 0 == mimicSeed {
		// mimic rate limit error

		if requestBucket.RetryCount < requestBucket.RetryLimit {
			requestBucket.RetryCount++
			param.wg.Add(1)

			g.Log().Infof(param.ctx, "rate limit")
			time.Sleep(time.Duration(200+rand.Intn(200)) * time.Millisecond) // [200, 400) ms

			param.bucketQueue.Push(requestBucket)
			triggerNextAction()
		} else {
			g.Log().Warningf(param.ctx, "retry limitation reached")

			requestBucket.Content = "failed after retrying 7 times"
			param.resultList.PushBack(requestBucket)
		}
		return
	}

	if 1 == mimicSeed {
		// mimic bucket truncated error
		g.Log().Infof(param.ctx, "bucket truncated")

		param.wg.Add(2)

		param.bucketQueue.Push(&TranslationRequestBucket{
			Content:    fmt.Sprintf("%s-%d-1", requestBucket.Content, time.Now().UnixMilli()),
			RetryLimit: 7,
			RetryCount: 0,
		})
		triggerNextAction()

		param.bucketQueue.Push(&TranslationRequestBucket{
			Content:    fmt.Sprintf("%s-%d-2", requestBucket.Content, time.Now().UnixMilli()),
			RetryLimit: 7,
			RetryCount: 0,
		})
		triggerNextAction()

		return
	}

	if 2 == mimicSeed {
		// mimic other error
		g.Log().Infof(param.ctx, "other error")

		if requestBucket.RetryCount < requestBucket.RetryLimit {
			requestBucket.RetryCount++

			param.wg.Add(1)
			param.bucketQueue.Push(requestBucket)

			triggerNextAction()
		} else {
			g.Log().Warningf(param.ctx, "retry limitation reached")

			requestBucket.Content = "failed after retrying 7 times"
			param.resultList.PushBack(requestBucket)
		}

		return
	}

	// not mimic the translation missing error
	g.Log().Infof(param.ctx, "Got It")

	requestBucket.Content += " OK"
	param.resultList.PushBack(requestBucket)
}

func Entrance(messageCount int) {
	ctx := context.Background()

	bucketQueue := gqueue.New()
	for i := 0; i < messageCount; i++ {
		bucketQueue.Push(&TranslationRequestBucket{
			Content:    fmt.Sprintf("message_%d", i),
			RetryLimit: 7,
			RetryCount: 0,
		})
	}

	responseBucketList := glist.New(true)
	poolOpenAiApi, _ := ants.NewPoolWithFunc(10, func(input interface{}) {
		CallOpenAiApi(input)
	})

	var wg sync.WaitGroup
	wg.Add(int(10 * bucketQueue.Len()))

	for i := 0; i < int(10*bucketQueue.Len()); i++ {
		_ = poolOpenAiApi.Invoke(&ApiCallParam{
			ctx:         ctx,
			bucketQueue: bucketQueue,
			resultList:  responseBucketList,
			wg:          &wg,
			pool:        poolOpenAiApi,
		})
	}

	wg.Wait()
	bucketQueue.Close()
	poolOpenAiApi.Release()
}
